﻿#include <QDebug>
#include <QFile>
#include <QNetworkProxy>
#include <private/qgeotilespec_p.h>

#include "QGeoTiledMapReplyCustom.h"
#include "MapEngine.h"
#include "MapEngineData.h"
#include "UrlFactory.h"
#include "MapProvider.h"

int         QGeoTiledMapReplyCustom::_requestCount = 0;
QByteArray  QGeoTiledMapReplyCustom::_bingNoTileImage;

QGeoTiledMapReplyCustom::QGeoTiledMapReplyCustom(QNetworkAccessManager *networkManager, const QNetworkRequest &request,
                                                 const QGeoTileSpec &spec, QObject *parent)
    : QGeoTiledMapReply(spec, parent)
    , _reply(nullptr)
    , _request(request)
    , _networkManager(networkManager)
{
    if (_bingNoTileImage.count() == 0) {
        QFile file(":/res/image/BingNoTileBytes.dat");
        if (file.open(QFile::ReadOnly)) {
            _bingNoTileImage = file.readAll();
            file.close();
        }
    }
    //如果请求地址为空,显示空白tile
    if (_request.url().isEmpty()) {
        if (!_badMapbox.size()) {
            QFile file(":/rc/image/notile.png");
            if (file.open(QFile::ReadOnly)) {
                _badMapbox = file.readAll();
                file.close();
            }
        }
        setMapImageData(_badMapbox);
        setMapImageFormat("png");
        setFinished(true);
        setCached(false);
    } else {
        // 如果地址不为空,从本地数据库获取tile
        //QString type = getMapEngine()->urlFactory()->getTypeFromId(spec.mapId());
        FetchTileTask* task = getMapEngine()->createFetchTileTask(spec.mapId(), spec.x(), spec.y(), spec.zoom());
        connect(task, &FetchTileTask::tileFetched, this, &QGeoTiledMapReplyCustom::_cacheReply);
        connect(task, &FetchTileTask::error, this, &QGeoTiledMapReplyCustom::_cacheError);
        getMapEngine()->addTask(task);  // FetchTileTask
    }
}

QGeoTiledMapReplyCustom::~QGeoTiledMapReplyCustom()
{
    _clearReply();
}

void QGeoTiledMapReplyCustom::abort()
{
    _timer.stop();
    if (_reply)
        _reply->abort();
    emit aborted();
}

void QGeoTiledMapReplyCustom::_networkReplyFinished()
{
    _timer.stop();
    // 可能已是获取超时
    if (!_reply) {
        emit aborted();
        return;
    }
    // 获取错误
    if (_reply->error() != QNetworkReply::NoError) {
        emit aborted();
        return;
    }

    // 获取到tile
    QByteArray a = _reply->readAll();
    UrlFactory* urlFactory = getMapEngine()->urlFactory();
    QString format = urlFactory->getImageFormat(tileSpec().mapId(), a);
    //-- Test for a specialized, elevation data (not map tile)
    // 判断是否是高程服务
    if (urlFactory->isElevation(tileSpec().mapId())) {
        ;
    } else {
        // 非高程数据
        MapProvider* mapProvider = urlFactory->getMapProviderFromId(tileSpec().mapId());
        // 可放大的图层
        if (mapProvider && mapProvider->isBingProvider() && a.size() && _bingNoTileImage.size() && a == _bingNoTileImage) {
            // Bing doesn't return an error if you request a tile above supported zoom level
            // It instead returns an image of a missing tile graphic. We need to detect that
            // and error out so Qt will deal with zooming correctly even if it doesn't have the tile.
            // This allows us to zoom up to level 23 even though the tiles don't actually exist
            setError(QGeoTiledMapReply::CommunicationError, "Bing tile above zoom level");
        } else {
            // 正常图层
            setMapImageData(a);
            if (!format.isEmpty()) {
                setMapImageFormat(format);
                // 缓冲到数据库
                getMapEngine()->cacheTile(urlFactory->getTypeFromId(tileSpec().mapId()), tileSpec().x(), tileSpec().y(), tileSpec().zoom(), a, format);
            }
        }
        setFinished(true);
    }

    _clearReply();
}

void QGeoTiledMapReplyCustom::_networkReplyError(QNetworkReply::NetworkError error)
{
    _timer.stop();
    if (!_reply) {
        return;
    }
    //-- Test for a specialized, elevation data (not map tile)
    if(getMapEngine()->urlFactory()->isElevation(tileSpec().mapId())) {
        emit terrainDone(QByteArray(), error);
    } else {
        //-- Regular map tile
        if (error != QNetworkReply::OperationCanceledError) {
            qWarning() << "Fetch tile error:" << _reply->errorString();
            setError(QGeoTiledMapReply::CommunicationError, _reply->errorString());
        }
        setFinished(true);
    }
    _clearReply();
    // my
    // setError(QGeoTiledMapReply::CommunicationError, _reply->errorString());
    // getMapEngine()->testInternet();
    // setFinished(true);
    // _clearReply();
}

/**
 * @brief 正常从数据库中获取到tile
 * @param tile
 */
// tag:useCacheTile
void QGeoTiledMapReplyCustom::_cacheReply(CacheTile *tile)
{
    //-- Test for a specialized, elevation data (not map tile)
    if( getMapEngine()->urlFactory()->isElevation(tileSpec().mapId())){
        emit terrainDone(tile->img(), QNetworkReply::NoError);
    } else {
        //-- Regular map tile
        setMapImageData(tile->img());
        setMapImageFormat(tile->format());
        setFinished(true);
        setCached(true);
    }
    tile->deleteLater();
}

void QGeoTiledMapReplyCustom::_cacheError(MapTask::TaskType type, QString errorString)
{
    if(!getMapEngine()->isInternetActive()) {
        if( getMapEngine()->urlFactory()->isElevation(tileSpec().mapId())){
            emit terrainDone(QByteArray(), QNetworkReply::NetworkSessionFailedError);
        } else {
            setError(QGeoTiledMapReply::CommunicationError, "Network not available");
            setFinished(true);
        }
    } else {
        if(type != MapTask::taskFetchTile) {
            qWarning() << "QGeoTiledMapReplyCustom::cacheError() for wrong task";
        }
        //-- Tile not in cache. Get it off the Internet.
#if !defined(__mobile__)
        QNetworkProxy proxy = _networkManager->proxy();
        QNetworkProxy tProxy;
        tProxy.setType(QNetworkProxy::DefaultProxy);
        _networkManager->setProxy(tProxy);
#endif
        _reply = _networkManager->get(_request);
        _reply->setParent(nullptr);
        connect(_reply, &QNetworkReply::finished, this, &QGeoTiledMapReplyCustom::_networkReplyFinished);
        connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(_networkReplyError(QNetworkReply::NetworkError)));
#if !defined(__mobile__)
        _networkManager->setProxy(proxy);
#endif
    //- Wait for an answer up to 10 seconds
        connect(&_timer, &QTimer::timeout, this, &QGeoTiledMapReplyCustom::_timeout);
        _timer.setSingleShot(true);
        _timer.start(10000);
        _requestCount++;
    }
}

void QGeoTiledMapReplyCustom::_timeout()
{
    if(_reply) {
        _reply->abort();
    }
    emit aborted();
    // my
    // _clearReply();
    // emit aborted();
}

void QGeoTiledMapReplyCustom::_clearReply()
{
    _timer.stop();
    if (_reply) {
        _reply->deleteLater();
        _reply = nullptr;
        _requestCount--;
    }
}
